#include <stdlib.h>
#include <iostream>
#include <iomanip>
#include <map>
#include <windows.h>
#include <tchar.h>
#include <string>
#include <sstream>

#include "basscd.h"
#include "BASSCDRipperDefs.h"

static bool f_ignoreZero = false;

std::map<int, std::string> getDrives();
static void __stdcall onProgress(
	LPVOID ripper, 
	UINT trackProgress, 
	UINT totalProgress);

using namespace std;

int main()
{
	BASS_Init(-1, 44100, 0, NULL, NULL);

	// 
	// enumerate all installed drives
	// 
	std::map<int, std::string> drives = getDrives();

	if (drives.empty())
	{
		cout << "Error: no drives present" << endl;
		return 0;
	}

	// 
	// print them out
	// 
	cout << "Drives:" << endl;
	for (std::map<int, std::string>::iterator it = drives.begin();
		it != drives.end();
		it++)
		cout << "\t" << it->first << ") " << ": " << it->second.c_str() << endl;

	// 
	// have the user pick one
	// 
	int drive = -1;
	while (drive < 0)
	{
		cout << "Choose a drive: ";
		cin >> drive;

		if (drives.find(drive) == drives.end())
		{
			cout << "Error: invalid drive" << endl;
			drive = -1;
		}
	}

	// 
	// close the door if it's open
	// 
	BASS_CD_Door(drive, BASS_CD_DOOR_CLOSE);
	
	std::string title;
	cout << "Book title: ";
	cin.ignore();
	getline(cin, title);

	std::string author;
	cout << "Author: ";
	getline(cin, author);

	int startDisc = 1;
	cout << "Start with disc: ";
	cin >> startDisc;

	BASSCDRipper::InitBASSCDRipperLibrary();

	// 
	// create our ripper
	// 
	LPVOID ripper = BASSCDRipper::BASSCDRipper_Create();
	if (!ripper)
	{
		cout << "Error: failed to create ripper object" << endl;
		return 0;
	}

	for (int i = startDisc;; i++)
	{
		// 
		// wait for the disc to be inserted
		// 
		cout << endl << "Waiting for drive to become ready for disc " << i << "..." << endl;
		for (;;)
		{
			if (!BASS_CD_IsReady(drive)
				|| BASS_CD_DoorIsOpen(drive))
			{
				Sleep(1000);
				continue;
			}

			break;
		}
		cout << "Drive ready!" << endl;

		// 
		// create our dir path
		// 
		if (!CreateDirectoryA(
			author.c_str(),
			NULL))
		{
			if (GetLastError() != ERROR_ALREADY_EXISTS)
			{
				cout << "Error: couldn't create directory for author" << endl;
				return 0;
			}
		}

		std::string titlePath = author + "\\" + title;
		if (!CreateDirectoryA(
			titlePath.c_str(),
			NULL))
		{
			if (GetLastError() != ERROR_ALREADY_EXISTS)
			{
				cout << "Error: couldn't create directory for title" << endl;
				return 0;
			}
		}

		// 
		// rip the cd
		// 
		std::stringstream prefixStream;
		prefixStream << author << "\\" << title << "\\Disc" << setfill('0') << setw(2) << i;
		std::string prefix = prefixStream.str();
		std::string wavFileName = prefix + ".wav";
		std::string mp3FileName = prefix + ".mp3";
		std::wstring prefixw(prefix.begin(), prefix.end());
		std::wstring wavFileNameW(wavFileName.begin(), wavFileName.end());
		std::wstring mp3FileNameW(mp3FileName.begin(), mp3FileName.end());
		f_ignoreZero = false;
		TBASSCDRipperParams params;
		::ZeroMemory(&params, sizeof(params));

		params.Drive = drive;
		params.AlbumMode = true;
		params.Format = FORMAT_WAV;

		params.FileName = (LPTSTR)wavFileNameW.c_str();

		cout << "Ripping..." << endl;

		BASSCDRipper::BASSCDRipper_SetParams(ripper, params);
		BASSCDRipper::BASSCDRipper_SetProgressCallback(ripper, onProgress);
		BASSCDRipper::BASSCDRipper_Ripp(ripper);
		
		// 
		// convert to mp3
		// 
		cout << endl << "Transcoding..." << endl;

		std::stringstream lameCmdStream;
		lameCmdStream << "lame.exe -V2 --ta \"" << author << "\" --tl \"" << 
			title << "\" \"" << wavFileName << "\" \"" << mp3FileName << "\"";
		system(lameCmdStream.str().c_str());

		// 
		// remove the wav
		// 
		DeleteFile(wavFileNameW.c_str());

		cout << "Done!" << endl;

		// 
		// pop out the drive
		// 
		BASS_CD_Door(drive, BASS_CD_DOOR_OPEN);
	}

	// 
	// free our ripper
	// 
	BASSCDRipper::BASSCDRipper_Free(ripper);

	BASSCDRipper::FreeBASSCDRipperLibrary();
	
	BASS_Free();

	return 0;
}

std::map<int, std::string> getDrives()
{
	std::map<int, std::string> drives;

	BASS_CD_INFO cdInfo;
	bool driveFound = true;
	for (int i = 0; driveFound; i++)
	{
		::ZeroMemory(&cdInfo, sizeof(BASS_CD_INFO));
		driveFound = BASS_CD_GetInfo(i, &cdInfo);
		if (driveFound)
			drives.insert(std::pair<int, std::string>(i, cdInfo.product));
	}

	return drives;
}

void __stdcall onProgress(
	LPVOID ripper, 
	UINT trackProgress, 
	UINT totalProgress)
{
	//cout << trackProgress << "(" << totalProgress << ") ";
	int position = 0;
	if (totalProgress)
	{
		f_ignoreZero = true;
		position = totalProgress / 5;
	}

	if (position == 0 && f_ignoreZero)
		return;

	cout << "\r|";
	for (int i = 0; i < position; i++)
		cout << "=";

	cout << "|";

	for (int i = position; i < 20; i++)
		cout << "=";
	cout << "|";
}